home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 48 / Amiga Format CD48 (1999-12-13)(Future Publishing)(GB)(Track 1 of 2)[!][issue 2000-01].iso / -in_the_mag- / networking / crosspc / parpc04 / packet / src / parsubr.asm < prev    next >
Assembly Source File  |  1993-09-01  |  25KB  |  885 lines

  1. ; PARSUBR.ASM
  2. ;
  3. ; Lowlevel routines for PARnet
  4. ; This is not the general PARnet-code. It will only work in combination
  5. ; with the PARnet packet driver.
  6. ;
  7. ; 03-JUN-93 First PC version of PARnet <S.A.Pechler@bdk.tue.nl>
  8. ; 03-JUL-93 Fixed bug when transferring odd number of bytes.
  9. ;           Optimized by putting some counters in registers <S.A.Pechler>
  10. ; 07-JUL-93 Bugfix: ES was not set when reading into overflow buffer.
  11. ;           Deleted leading underscores from all data labels.
  12. ;           Added ParReadV and ParWriteV functions.         <S.A.Pechler>
  13. ; 26-JUL-93 Changed this file to get it work with the PARnet packet
  14. ;           driver (combined ParReadV & ParWrite into ParRead & ParWrite).
  15. ; 01-SEP-93 Optimized the functions by using block move operations (STOSB).
  16. ;           Changed the register usage of read_data() and write_data().
  17. ; 02-SEP-93 Rewrote all private routines for usage without the special
  18. ;           PARnet interface (connections between 2 machines only).
  19. ;
  20. ;
  21. ;   PARALLEL PORT NETWORK LOW LEVEL ROUTINES
  22. ;
  23. ;   THE CABLE:
  24. ;                  (2-9)   D7-D0  ----------  D7-D0 (2-9)
  25. ;        PC        ( 1)    STROBE ----------  BUSY  (11)      AMIGA
  26. ;     PARALLEL  <= (14)    AUTOLF ----------  POUT  (12)  => PARALLEL
  27. ;       CARD       (16)    INIT   --+----+--  SEL   (13)       PORT
  28. ;                  (10)    ACK    -/      \-  ACK   (10)
  29. ;                  (18-22) GND    ----------  GND   (18-22)
  30. ;
  31. ;      Cable connections between PC and Amiga WITHOUT(!) an interface
  32. ;
  33. ;
  34. ;
  35. ;                 (2-9)   D7-D0  ----------  D7-D0  (2-9)
  36. ;        PC        ( 1)    STROBE ----------  STROBE ( 1)        PC
  37. ;     PARALLEL  <= (14)    AUTOLF ----------  AUTOLF (14)  => PARALLEL
  38. ;       CARD       (16)    INIT   --+----+--  INIT   (16)       CARD
  39. ;                  (10)    ACK    -/      \-  ACK    (10)
  40. ;                  (18-22) GND    ----------  GND    (18-22)
  41. ;
  42. ;       Cable connections between 2 PC's WITHOUT(!) an interface
  43. ;
  44. ;
  45. ;   Parallel port usage:
  46. ;
  47. ;   DATA PORT   : used for data output only
  48. ;   STATUS PORT : used for data & control input
  49. ;   CONTROL PORT: used for control output
  50. ;
  51. ;   All lines pulled up.  Thus, asserted state is a 0.    Idle
  52. ;   state is an undriven (1).  Protocol transfers a byte at
  53. ;   a time.  Protocol is ethernet style with a small window
  54. ;   of error in the line aquisition routine.
  55. ;
  56. ;   Note:   Timeouts should be set around a second.  Ideally
  57. ;        defaults should be fixed on faster machines.
  58. ;
  59. ;   LINE description:
  60. ;
  61. ;   PARnet: Input: Output:  Description:
  62. ;   ---------------------------------------------------------
  63. ;   ~ACK    SLCT   STROBE   hand shake
  64. ;   ~REQ    POUT   AUTOLF   hand shake
  65. ;    CTL    ACK    INIT     1 = special byte, 0 = data byte.
  66. ;                (for address mark, valid when ~REQ
  67. ;                 goes low.    For EOP mark, sample
  68. ;                 when ~REQ goes high)
  69. ;
  70. ;   See the associated routines for the exact low-level protocols.
  71. ;
  72. ;
  73. ;   PROTOCOL DESCRIPTION:
  74. ;
  75. ;   HandShake    (Reader <- Writer transfer).  A Handshake
  76. ;        sequence transfers TWO bytes of information.
  77. ;
  78. ;        WRITER                READER
  79. ;    |-> place data, ~REQ->0
  80. ;    |                 wait for ~REQ->0
  81. ;    |                 read data & store
  82. ;    |                 set ~ACK->0
  83. ;    |   wait ~ACK->0
  84. ;    |   place data, ~REQ->1
  85. ;    |                 wait for ~REQ->1
  86. ;    |                 read data & store
  87. ;    |                 set ~ACK->1
  88. ;    |   wait ~ACK->1
  89. ;    |<- LOOP  (2 bytes written)      LOOP  (2 bytes read)
  90. ;
  91. ;
  92. ;   Read:   (1) Determine if your machine is being addressed
  93. ;        (~REQ=0, data=myaddress, CTL=1)
  94. ;        (1) set ~ACK to output and
  95. ;        (2) Handshake sequence for the address mark, only
  96. ;        first byte valid.
  97. ;        (3) Handshake sequence for data util rcv byte
  98. ;        with CTL = 1 (EOP), byte must == 0.
  99. ;        (4) Set ~ACK to input
  100. ;
  101. ;   Write:  (1) AQUIRE THE NETWORK (see below)
  102. ;        involves gaining control and then setting the
  103. ;        CTL and ~REQ to outputs.
  104. ;
  105. ;        Also checks if somebody is writing to us,
  106. ;        in which case -2 is returned instantaniously
  107. ;        indicating we should do a ParRead().
  108. ;
  109. ;        (2) Handshake sequence for address mark, send dest
  110. ;        address (second byte garbage).  Note that CTL->1
  111. ;        *BEFORE* we set ~REQ->0
  112. ;        (3) Handshake sequence for data bytes
  113. ;        (4) Handshake sequence for EOP mark (Note that CTL->1
  114. ;        *AFTER* we get the ~ACK->1 and before release
  115. ;        ~REQ (->1).  Only firstbyte valid and set to 0.
  116. ;
  117. ;        (5) Set ~ACK and ~CTL to inputs
  118. ;
  119. ;   AQUIRE: Line aquisition prevents two people from writing to
  120. ;        the net at the same time.
  121. ;
  122. ;        * A line is considered aquired if ANY of the 3
  123. ;          control lines is 0.
  124. ;        * If network is not aquired:
  125. ;        - set ~ACK to output and 1
  126. ;        - bclr ~ACK to 0 and bne success
  127. ;          (else set ~ACK to input and try again)
  128. ;        - set data lines to output and place my address
  129. ;          (Must ]be done before CTL is glitched)
  130. ;        - set ~CTL to output and 1
  131. ;        - set CTL to 0 and then 1 (glitch it) to cause
  132. ;          FLAG interrupt on all other machines
  133. ;        - set ~REQ to output and 0 (beginning of handshake
  134. ;          sequence)
  135. ;        - lastly, release ~ACK by setting it to an input
  136. ;
  137. ;          Note that at all times at least one line is a 0
  138. ;          so no other machine will attempt to aquire the net.
  139. ;
  140. ;   Note that the destination address is placed on the data
  141. ;   lines after we have aquired the line but before we glitch
  142. ;   the CTL line to cause an interrupt.  This allows the
  143. ;   other machines to instantaniously determine who is being
  144. ;   addressed.
  145. ;
  146. ;   Note that the CTL line gets glitched at the end of a packet
  147. ;   too for the EOP mark.  In this case there is a 0 on the
  148. ;   data lines so while an interrupt is generated, nobody
  149. ;   thinks they are being addressed.
  150. ;
  151.  
  152. Par_EthAddr     db 0,0,0,0,0    ; to align the 6 octets of the fake
  153.                 ; PARnet "ethernet" address.
  154. ParNetAddr    db 1,0,0,0    ; my PARnet address (default), the trailing
  155.                 ; 3 zero's are padding for the get_number
  156.                 ; function.
  157.  
  158. LPTData        dw 0378h    ; LPT data port address (default)
  159. LPTStatus    dw 0379h    ; LPT status port address (default)
  160. LPTControl    dw 037ah    ; LPT control port address (default)
  161.  
  162. ParLLTimeout    dw 65535
  163.     ;should be dl 983025    ; default timeout value (about 1 second?)
  164. Dummybuf    db 0        ; dummy buffer
  165.         db 0        ; dummy buffer
  166.         db 0,0        ; padding
  167.  
  168.  
  169. ;============================================================================
  170. ;Private Routines
  171. ;
  172. ;Don't use these in any program.
  173.  
  174.  
  175. ;----------------------------------------------------------------------------
  176. ; Stable
  177. ;
  178. ; Put the interface in a stable mode by setting the data and control lines
  179. ; to high.
  180. ;
  181. stable:
  182.     push ax
  183.     push dx
  184.     mov dx,LPTData      ;data register
  185.     mov al,-1      ;datalines high
  186.     out dx,al      ;set it
  187.     mov dx,LPTControl ;Control register
  188.     mov al,14h      ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  189.     out dx,al      ;write control
  190.     pop dx
  191.     pop ax
  192.     ret
  193.  
  194. ;----------------------------------------------------------------------------
  195. ;Data Input
  196. ;
  197. ; Set the datalines to 'input' by setting all lines to high
  198. ;
  199. data_input:
  200.     push ax
  201.     push dx
  202.     mov dx,LPTData    ;data register
  203.     mov al,-1    ;datalines high
  204.     out dx,al    ;set it
  205.     pop dx
  206.     pop ax
  207.     ret
  208.  
  209. ;----------------------------------------------------------------------------
  210. ; Control Input
  211. ;
  212. ; Set the control-lines to 'input' by setting all lines to high
  213. ;
  214. ;control_input:
  215. ;    push ax
  216. ;    push dx
  217. ;    mov dx,LPTControl ;Control register
  218. ;    mov al,14h      ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  219. ;    out dx,al      ;set it
  220. ;    pop dx
  221. ;    pop ax
  222. ;    ret
  223.  
  224. ;----------------------------------------------------------------------------
  225. ; Read Data
  226. ;
  227. ; read data from the cable, put it in AL
  228. ;
  229. read_data:        ;data line must be set to INPUT first!
  230.     push dx
  231.     mov dx,LPTData    ;data register
  232.     in al,dx    ;read it
  233.     pop dx
  234.     ret
  235.  
  236. ;----------------------------------------------------------------------------
  237. ; Read Control
  238. ;
  239. ; Read the controlbits from the cable (busy, pout & sel)
  240. ;
  241. ; BL: control read from control register, represents the REAL line
  242. ;     status (so if a line is +5V, then the bit is 1).
  243. ;
  244. ; BL bits: 7654 3210  centronics:    cable:  parnet:
  245. ; value:   XXXX X100 
  246. ;          |||| |||`- strobe          busy    ack
  247. ;          |||| ||`-- auto LF         pout    req
  248. ;          |||| |`--- init            sel     ctl
  249. ;          |||| `---- selin            -       -
  250. ;          ````------ invalid          -       -
  251. ;
  252. read_control:            ;control lines must be set to input first!
  253.     push ax
  254.     push dx
  255.     mov dx,LPTControl    ;Control register
  256.     in al,dx        ;read it
  257.     xor al,0bh        ;invert all but init
  258.     and al,07        ;discard bits not needed
  259.     mov bl,al        ;return value in BL
  260.     pop dx
  261.     pop ax
  262.     ret            ;ready, controlbits in BL
  263.  
  264. ;-----------------------------------------------------------------------------
  265. ;Clear ACK Only
  266. ;
  267. ; Clear the parnet ack-bit. Leave other control lines high.
  268. ; Warning: I can't read my own control-lines back!
  269. ;
  270. clear_ack_only:
  271.     push bx
  272.     mov bl,0feh        ; set parnet ack-bit to 0
  273.     call write_control    ; write control
  274.     pop bx
  275.     ret
  276.  
  277. ;----------------------------------------------------------------------------
  278. ;Set ACK All
  279. ;
  280. ; Set the parnet ack-bit. Leave other control lines high.
  281. ; Warning: I can't read my own control-lines back!
  282. ;
  283. Set_ack_all:
  284.     push bx
  285.     mov bl,0fh        ; set all bits to 1 (including ACK)
  286.     call write_control    ; write control
  287.     pop bx
  288.     ret
  289.  
  290. ;----------------------------------------------------------------------------
  291. ; Write control
  292. ;
  293. ; BL: controlbits to be written
  294. ;
  295. ; BL bits: 7654 3210  centronics
  296. ; value:   XXXX X100  keyword:   cable:  controlbits:
  297. ;          |||| |||`- strobe      busy      ack
  298. ;          |||| ||`-- auto LF     pout      req
  299. ;          |||| |`--- init        sel       ctl
  300. ;          |||| `---- selin        -         -
  301. ;          ````------ invalid      -         -
  302. ;
  303. ; The value in BL represents the REAL LINE STATUS, so a bit=0 means the
  304. ; line is on low voltage.
  305. ;
  306. ; The INIT output is on the parallel card not inverted. This bit will be
  307. ; inverted in this procedure, so you don't have to care about it.
  308. ;
  309. write_control:
  310.     push ax
  311.     push dx
  312.     mov dx,LPTControl ; control register
  313.     mov al,bl      ; move control bits to AL (for OUT-instruction)
  314.     xor al,0bh      ; invert all lines except for INIT (=ctl)
  315.     out dx,al      ; write control
  316.     pop dx
  317.     pop ax
  318.     ret
  319.  
  320. ;----------------------------------------------------------------------------
  321. ; Write DATA
  322. ;
  323. ; Put Data on the line.
  324. ;
  325. ; AL: data to be written.
  326. ;
  327. write_data:            ; interface must be in stable mode!
  328.     push dx
  329.     mov dx,LPTData        ; data register
  330.     out dx,al        ; put data on line
  331.     pop dx
  332.     ret
  333.  
  334.  
  335. ; Function: paraddress
  336. ; Parameters: io_addr
  337. ;
  338. ; Set my LPT port address (0378h,03bch or 0278h)
  339. ;
  340. paraddress:
  341.     mov ax,io_addr        ; LPT port address
  342.     mov LPTData,ax        ; Place data port address
  343.     inc ax            ; next register is status port
  344.     mov LPTStatus,ax
  345.     inc ax            ; next register is control port
  346.     mov LPTControl,ax
  347.     call stable        ; all lines high
  348.     ret
  349.  
  350. ; Function: pardataready
  351. ; Parameter: none
  352. ;
  353. ; Check for data present (e.g. after an IRQ7).
  354. ;
  355. ; Returns: AL =  1 if packet is probably pending for you
  356. ;          AL =  0 if line is currently idle
  357. ;          AL = -1 if packet isn't for you
  358. ;
  359. ; If line has been aquired but no control address has been
  360. ; put on it yet, pardataready() will wait for a control
  361. ; address.  Thus, after a signal, a single call to
  362. ; pardataready() should suffice.
  363. ;
  364. pardataready:
  365.     call stable        ; be sure all lines are set to input
  366. .pdstable:
  367.     call read_control    ; read control in BL
  368.     mov cl,bl        ; save it
  369.     call read_data        ; read data in AL
  370.     call read_control    ; read control in BL
  371.     cmp cl,bl
  372.     jne .pdstable
  373.  
  374.     ;   Now, pardataready might be called after the sending machine
  375.     ;   has aquired but before it can assert REQ.  However, the
  376.     ;   sending machine has already (guarenteed) placed its address
  377.     ;   on the data port.  So while the address matches, loop while
  378.     ;   REQ not asserted.
  379.  
  380.     test bl,02        ; ~REQ asserted?
  381.     jz .pd10        ; yes
  382.     cmp  [ParNetAddr],al    ; no, does data match anyway?
  383.     je .pdstable        ; YES, loop until get ~REQ or
  384.     jmp short .pdfail    ; data bad.
  385.  
  386. .pd10:
  387.     test bl,04        ; yes, CTL?
  388.     jz .pdrn        ; no, middle of some packet.
  389.     cmp [ParNetAddr],al    ; yes, my address?
  390.     jne .pdrn        ; nope
  391.     mov al,1        ; yes, packet (probably) for us.
  392.     jmp short .pdend
  393.  
  394. .pdfail:
  395.     test bl,04        ; fail due to ~REQ not asserted.
  396.     jz .pdrn        ; CTL=0, line busy
  397.     xor al,al        ; line idle.
  398.     jmp short .pdend
  399.  
  400. .pdrn:
  401.     mov al,-1        ; line busy, packet not for me.
  402. .pdend:
  403.     ret
  404.  
  405. ;Function: parread
  406. ;Parameters: rx_buffer
  407. ;            rx_length
  408. ;
  409. ;Read a pending packet.
  410. ;
  411. ;Returns: AX = -1 (1 second timeout, no packet pending)
  412. ;       AX = 0 to bytes-1 (1 second timeout after transmission interrupted)
  413. ;      AX = bytes (success), or n > bytes (transmitting machine's packet
  414. ;          was larger than we can handle, extra bytes thrown out)
  415. ;
  416. ;NOTE:    Requesting an odd number of bytes is O.K. but if you request N where
  417. ;    N is odd and the writer sends N + 1 you will never know (N will be
  418. ;    returned). See also parwrite() below.
  419. ;
  420. parread:
  421.     push si
  422.     push di
  423.         push es
  424.         mov ax,ds
  425.         mov es,ax               ; mov es,ds (for STOSB instruction)
  426.         mov di,OFFSET rx_buffer ; es:di = buf
  427.  
  428.     mov si,BUFSIZE        ; maximum number of bytes to read.
  429.  
  430.     call stable        ; ensure line not asserted.
  431.     mov DX,[ParLLTimeout]    ; DX = timeout load
  432.  
  433.     ;  Wait loop for address mark
  434.     ;  Ctl = 1, ~DReq = 0
  435. .rmstab:
  436.     call read_control    ; read control in BL
  437.     mov cl,bl        ; save it
  438.     call read_data        ; read data in AL
  439.     call read_control    ; read control in BL
  440.     cmp cl,bl        ; control lines stable?
  441.     jne .rmstab        ; nope
  442.     test bl,04        ; expect CTL=1
  443.     jz .rms1        ; nope
  444.     test bl,02        ; expect ~REQ=0
  445.     jz .rms2        ; yes
  446.  
  447. .rms1:
  448.     dec dx            ; decrement timeout count
  449.     jnz .rmstab        ; no timeout.
  450.         mov ax,-1        ; return value: read timeout
  451.     jmp .rmend        ; no address mark!
  452.  
  453. .rms2:
  454.     cmp [ParNetAddr],al    ; My address?
  455.     jne .rms1        ; no, timeout loop
  456.  
  457.     ; Got my address, ~Ack byte.
  458.  
  459.     mov DX,[ParLLTimeout]    ; reset timeout
  460.     call Clear_Ack_Only    ; set ~Ack to 0
  461.  
  462. .rms4:  call read_control    ; get control
  463.     test bl,02        ; wait for ~REQ to go away
  464.     jnz .rms5
  465.     dec dx            ; decrement timeout count
  466.     jnz .rms4
  467.         mov ax,-2               ;~REQ not released?
  468.     jmp .rmend
  469.  
  470. .rms5:  call Set_Ack_All    ; release ~ACK
  471.  
  472.     mov cx,0        ; set # of bytes read to 0
  473.     jmp .rms10        ; skip past move
  474.  
  475.  
  476.     ; MAIN READ LOOP
  477.     ;
  478.     ; CX holds count (number of bytes read).
  479.     ; DI buffer pointer (place to store data).
  480.  
  481. .rms10loop:
  482. ;    mov es:[di],al        ; store data
  483. ;    inc di            ; next address
  484.     stosb
  485.  
  486. .rms10:
  487.     call Read_Control
  488.     test bl,02        ; wait for ~REQ asserted
  489.     jz .rms20
  490.     call Read_control
  491.     test bl,02        ; again
  492.     jz .rms20
  493.     mov DX,[ParLLTimeout]    ; reset timeout
  494.  
  495. .rms11: call Read_Control
  496.     test bl,02        ; wait for ~REQ asserted with timeout
  497.     jz .rms20
  498.     dec dx            ; decrement timeout count
  499.     jnz .rms11
  500.         mov ax,-1
  501.     jmp .rmend        ; timeout
  502.  
  503. .rms20: call read_data        ; get data in CL and
  504.     call Clear_Ack_Only    ; assert ~ACK
  505.  
  506.     ; note on CTL = 1 end sequence this data item is a dummy
  507.  
  508. ;        mov es:[di],al          ; store data
  509. ;    inc di            ; next address
  510.     stosb
  511.  
  512.     inc cx            ; optimized, but not quite true,
  513.     inc cx            ; we've only written one 1 sf.
  514.  
  515.     call Read_Control
  516.     test bl,02        ; wait for ~REQ released
  517.     jnz .rms30
  518.     call Read_Control
  519.     test bl,02        ; again
  520.     jnz .rms30
  521.     mov DX,[ParLLTimeout]    ; reset timeout
  522. .rms21:
  523.     call Read_Control
  524.     test bl,02        ; wait for ~REQ released with timeout
  525.     jnz .rms30
  526.     dec dx            ; decrement timeout count
  527.     jnz .rms21        ; no timeout yet?
  528.     jmp short .rmendsub    ; sub because D6 is 2 ahead
  529.  
  530.  
  531. .rms30:
  532.     call read_data        ; get data in CL
  533.     call Read_Control    ; get CTL status in BL
  534.     call Set_Ack_All    ; release ~ACK    
  535.     test bl,04        ; EOP if CTL=1
  536.     jnz .rmeop
  537.  
  538.     ; CANNOT STORE DATA HERE! In case odd # bytes requested,
  539.     ; second byte would overflow buffer (each handshake sequence ALWAYS
  540.     ; transfers 2 bytes of information)
  541.  
  542.     dec  si            ; # of bytes remaining
  543.     jz   .rmodd        ; already zero, # of bytes were odd.
  544.     dec  si
  545.  
  546.     jz  .rmste        ; reached zero (even bytes)
  547.     jmp .rms10loop        ; continue if >0.
  548.  
  549. .rmodd: dec si            ; si must be -1 when odd # bytes requested.
  550.     dec cx            ; fixup count (was one ahead)
  551.     jmp short .rmeven
  552.  
  553. .rmste:
  554.         mov [di],al        ; if si = 0, its's even and we
  555.                 ; should store the last byte.
  556. .rmeven:
  557. .rmovflow:
  558.     mov di,OFFSET Dummybuf    ; overflow, use dummy buffer
  559.  
  560.     jmp .rms10
  561.  
  562. .rmeop:
  563.     cmp al,0        ; EOP data better be 0!
  564.     je .rmendsub
  565.         mov ax,-3               ; bad protocol
  566.     jmp short .rmend
  567.  
  568. .rmendsub:
  569.     dec cx            ; because we were two ahead
  570.     dec cx
  571.     mov ax,cx        ; return value in AX
  572. .rmend:
  573.     call data_input
  574.     call Set_Ack_All    ; setting ~ACK to input
  575.     call stable
  576.         pop es
  577.     pop di
  578.     pop si
  579.     ret            ; return value in AX
  580.  
  581. ;Function: parwrite
  582. ;Parameters: tx_dest
  583. ;            tx_header
  584. ;            tx_hdrsize (=8)
  585. ;            tx_buffer
  586. ;            tx_dlen
  587. ;
  588. ;Write a packet.
  589. ;
  590. ;Returns: AX = -2       Cannot write anything, a packet is pending
  591. ;            (instantanious)
  592. ;         AX = -1       Destination machine does not respond (1 sec timeout)
  593. ;         AX = N        N bytes written ok (success if n == bytes)
  594. ;
  595. ; NOTE: sending an odd number of bytes is O.K. but if you write N where
  596. ;    N is odd and the reader requests N + 1 he will get N + 1 the last
  597. ;    byte being garbage.
  598. ;
  599. parwrite:
  600.     push es
  601.     push si
  602.     push di
  603.     mov ax,ds
  604.     mov es,ax        ; to simulate mov es,ds
  605.         mov si,OFFSET tx_header    ; es:si = buf
  606.  
  607.     mov di,tx_hdrsize    ; number of bytes to write.
  608.  
  609.  
  610.     call stable        ; ensure line not asserted.
  611.     mov DX,[ParLLTimeout]    ; DX = timeout load
  612.  
  613. .wmstab:
  614.     cli            ; disable interrupts
  615.     call Read_Control
  616.     mov cl,bl        ; save it
  617.     call read_data        ; read data in AL
  618.     call Read_Control    ; get stable control in BL
  619.     cmp cl,bl
  620.     je .wmstab1
  621.     sti            ; set interrupts
  622.     jmp short .wmstab
  623.  
  624.     ; Interrupts still disabled
  625.     ; BX holds ~ACK ~REQ and CTL status
  626.  
  627. .wmstab1:
  628.     mov ax,-2        ; number of bytes written yet (=none).
  629.     cmp bl,07h        ; ~ACK=1, ~REQ=1, CTL=1 ?
  630.     je .wm02
  631.  
  632.     ; if CTL=1, ~REQ=0 and CL=my address then
  633.     ; return with -2
  634.  
  635.     test bl,02h        ; ~REQ = 0?
  636.     jne .wm01        ; nope
  637.     test bl,04h        ; CTL = 1?
  638.     je .wm01        ; nope
  639.     cmp [ParNetAddr],al    ; somebody is calling me?
  640.     jne .wm01        ; nope
  641.  
  642.     sti            ; enable interrupts
  643.     jmp .wmend
  644.  
  645. .wm01:
  646.     sti            ; enable interrupts
  647.     dec dx            ; decrement timeout count
  648.     jnz .wmstab
  649.     jmp .wmend
  650.  
  651.     ; interrupts still disabled
  652.     ; we almost own the line
  653.  
  654. .wm02:
  655.     call Read_Control
  656.     call Clear_Ack_Only    ; assert ~ACK
  657.     test bl,01
  658.     jne .wm05        ; ACK was released before, have line!
  659.  
  660.     call Set_Ack_All    ; release ~ACK
  661.     jmp short .wm01
  662.  
  663.     ; Line now aquired.
  664.  
  665. .wm05:
  666.     sti            ; enable interrupts
  667.         mov al,tx_dest
  668.     call Write_Data        ; set destination address on datalines.
  669.  
  670.     ; Before asserting ~REQ, pulse CTL to cause interrupt on remote
  671.     ; machines. Note that our address is already on the datalines.
  672.  
  673.     mov bl,02h        ; leave ~ACK=0 and ~REQ=1, but CTL->0
  674.     call Write_Control
  675.     mov bl,06h        ; leave ~ACK=0 and ~REQ=1, but CTL->1
  676.     call Write_Control
  677.     mov bl,04h        ; leave ~ACK=0 and CTL=1, but ~REQ->0  
  678.     call Write_Control    ; assert REQ
  679.     mov bl,05h        ; leave CTL=1 and ~REQ=0, but ~ACK->1
  680.     call Write_Control    ; release ACK
  681.                 ; (note that REQ->0 before ACK->released)
  682.  
  683.     mov ax,-1        ; number of bytes written yet (none).
  684.  
  685.     ; interrupts enabled for transfer (fully handshaked)
  686.     ;
  687.     ; Address mark ~ACK, wait for ~ACK asserted.
  688. .wm10:
  689.     call Read_Control
  690.     test bl,01        ; ~ACK asserted?
  691.     jz .wm15        ; yes, remote machine got my address mark
  692.     mov DX,[ParLLTimeout]    ; DX = timeout count
  693. .wm11:
  694.     call Read_Control
  695.     test bl,01
  696.     jz .wm15
  697.     dec dx            ; decrement timeout count
  698.     jnz .wm11        ; timeout?
  699.     jmp .wmend        ; yes.
  700.  
  701.     ; got ack, now set CTL = 0 (leaves at least one line 0 so
  702.     ; nobody else thinks the bus is idle!)
  703.     ;
  704.     ; note:  Since this is the address mark, and is sampled by
  705.     ;     the reader before it asserts ~ACK, I can set CTL
  706.     ;     = 0 now instead of waiting till after ~ACK is
  707.     ;     released.
  708.  
  709. .wm15:
  710.     mov bl,01h        ; leave ACK=1, REQ=0, but CTL->0
  711.     call Write_Control    ; set CTL = 0 for duration of packet
  712.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  713.     call Write_Control    ; release ~REQ
  714.  
  715.     call .wm19        ; Send header
  716.     cmp ax,tx_hdrsize    ; all bytes written?
  717.     je .wm16        ; yes, ok
  718.     jmp .wmend        ; no, then exit with error
  719. .wm16:  
  720.         les si,tx_data      ; es:si = buf
  721.         mov di,tx_length        ; number of bytes to write.
  722.     call .wm19        ; Send data
  723.     cmp ax,tx_length    ; all bytes written?
  724.     je .wm17        ; yes, ok
  725.     jmp .wmend        ; no, then exit with error
  726. .wm17:
  727.         jmp .wm50a              ; all bytes transmitted, send an EOP.
  728.  
  729.  
  730.     ; Data transfer loop
  731.     ;
  732.     ; wait for ~ACK to be released (-> 1). If no more bytes to
  733.     ; write, then skip to .wm50
  734.  
  735. .wm19:
  736.     mov cx,0        ; number of bytes written (none).
  737.  
  738. .wm20:
  739.     or  di,di        ; more data in this buffer?
  740.     jz .wm50        ; nope
  741.  
  742.     call Read_Control
  743.     test bl,01        ; wait for ~ACK to be released
  744.     jnz .wm30
  745.     mov DX,[ParLLTimeout]    ; DX = timeout countdown
  746. .wm21:
  747.     call Read_Control
  748.     test bl,01
  749.     jnz .wm30        ; need the timeout here?
  750.     call Read_control
  751.     test bl,01        ; check ACK again
  752.     jnz .wm30
  753.     dec dx
  754.     jnz .wm21
  755.     mov ax,cx        ; return value in AX
  756.     ret            ; timeout
  757.  
  758.     ; Assert ~REQ for this data byte and wait for ~ACK
  759.  
  760. .wm30:
  761.         mov al,es:[si]        ; get next data byte.
  762.     inc si            ; this can't be done with a LODSB :-(
  763.  
  764.     call Write_Data        ; store data and ..
  765.     mov bl,01h        ; leave ACK=1, CTL=0, but REQ->0
  766.     call Write_Control    ; .. assert ~REQ
  767.  
  768.         mov al,es:[si]          ; get next data bytes
  769.     inc si            ; this can't be done with a LODSB :-(
  770.  
  771.     inc cx            ; number of bytes written (this only)
  772.                 ; (not valid until we get ACK which
  773.                 ;  is why the wmendsub is included)
  774.  
  775.     call Read_Control
  776.     test bl,01        ; wait for ACK asserted
  777.     jz .wm40
  778.     call Read_Control
  779.     test bl,01        ; look again
  780.     jz .wm40
  781.     mov DX,[ParLLTimeout]    ; DX = timeout count
  782. .wm31:
  783.     call Read_Control
  784.     test bl,01        ; wait for ACK asserted with timeout
  785.     jz .wm40
  786.     dec dx            ; decrement timeout count
  787.     jnz .wm31
  788.     dec cx            ; timeout, CX was ahead in count
  789.     mov ax,cx        ; return value in AX
  790.     ret            ; timeout
  791.  
  792.     ; have ~ACK, so byte transmitted. increment bytes written,
  793.     ; decr. of bytes left was already done before.
  794.     ; now send second byte and loop back.
  795.  
  796. .wm40:    call Write_Data        ; data was loaded in AL before.
  797.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  798.     call Write_Control    ; release ~REQ
  799.     inc cx            ; increment number of bytes written
  800.  
  801.     dec di            ; one less bytes
  802.     jz .wm40a        ; odd number of bytes were written
  803.     dec di            ; one less bytes
  804.     jz .wm50        ; these were the last even bytes(s)
  805.  
  806.     jmp .wm20        ; loop back
  807.  
  808. .wm40a: dec di            ; fixup, di = -1 when odd bytes requested
  809.  
  810.     ; Last byte in this buffer has been transmitted.
  811.     ; Get next buffer.
  812.  
  813.     ; Add DI to CX. This handles fixup if an odd number of bytes were
  814.     ; requested written, DI will be -1 (odd) or 0 (even) and CX will 
  815.     ; be one too large (odd) or perfect (even)
  816.  
  817.         add cx,di               ; adjust CX when odd bytes requested
  818. .wm50:
  819.     mov ax,cx        ; return value in AX
  820.         ret
  821.  
  822.  
  823.  
  824. .wm50a:
  825.     ;   Last byte has been transmitted,
  826.     ;
  827.     ;   Wait for ~ACK to be released and then assert ~REQ with
  828.     ;   EOP & CTL = 1
  829.     ;
  830.     ;   (timing on read is that CTL is sampled when ~REQ is
  831.     ;   RELEASED so no timing window here)
  832.  
  833.     mov cx,ax        ; save previous value of AX
  834.     call Read_Control    ; Wait ~ACK released
  835.     test bl,01
  836.     jz .wm50a        ; loop back
  837.  
  838.     mov al,0
  839.     call Write_Data        ; EOP mark (=0)
  840.     mov bl,01h        ; leave ACK=1 and CTL=0, but REQ->0
  841.     call Write_Control    ; assert ~REQ
  842.  
  843.     ; Wait for ~ACK asserted
  844.  
  845.     call Read_Control
  846.     test bl,01        ; ACK asserted?
  847.     jz .wm60        ; yes
  848.     mov DX,[ParLLTimeout]    ; DX = timeout count
  849. .wm51:
  850.     call Read_Control
  851.     test bl,01        ; wait for ACK asserted with timeout
  852.     jz .wm60
  853.     dec dx            ; decrement timeout count
  854.     jnz .wm51
  855.     mov ax,-3        ; EOP failed
  856.     jmp short .wmend
  857.  
  858.     ; Set CTL=1 then release ~REQ, then wait for ~ACK released
  859. .wm60:
  860.     mov bl,05h        ; leave ACK=1, REQ=0, but CTL->1
  861.     call Write_Control    ; release CTL
  862.     mov bl,07h        ; leave ACK=1, CTL=1, but REQ->1
  863.     call Write_Control    ; release ~REQ
  864.     
  865.     ; Wait ~ACK released ?
  866.  
  867. .wm61:    call Read_Control
  868.     test bl,01
  869.     jz .wm61
  870.  
  871.     ; Add DI to CX. This handles fixup if an odd number of bytes were
  872.     ; requested written, DI will be -1 (odd) or 0 (even) and CX will 
  873.     ; be one too large (odd) or perfect (even)
  874.  
  875. ;        add cx,di
  876.  
  877.     mov ax,cx        ; return value in AX
  878. .wmend:
  879.     call stable        ; set all lines to input
  880.     pop di
  881.     pop si
  882.     pop es
  883.     ret            ; return value in AX
  884.  
  885.